package cn.stalk.compare;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;

public abstract class DataAccessor<T, V> {

    protected final String name;
    protected boolean blankToNull;
    protected boolean trim;
    protected String splitToList;
    protected boolean toSet;
    protected boolean toNumber;
    protected boolean useCompare = true;
    protected boolean nullsLast = true;

    protected DataAccessor(String name) {
        this(name, true, false, null, true);
    }

    protected DataAccessor(String name, boolean blankToNull, boolean trim, String splitToList, boolean toSet) {
        this.name = name;
        this.blankToNull = blankToNull;
        this.trim = trim;
        this.splitToList = splitToList;
        this.toSet = toSet;
    }

    public String getName() {
        return name;
    }

    public void setToNumber(boolean toNumber) {
        this.toNumber = toNumber;
    }

    public void setBlankToNull(boolean blankToNull) {
        this.blankToNull = blankToNull;
    }

    public void setSplitToList(String splitToList) {
        this.splitToList = splitToList;
    }

    public void setToSet(boolean toSet) {
        this.toSet = toSet;
    }

    public void setTrim(boolean trim) {
        this.trim = trim;
    }

    public void setNullsLast(boolean nullsLast) {
        this.nullsLast = nullsLast;
    }

    public boolean isToSet() {
        return toSet;
    }

    public boolean isBlankToNull() {
        return blankToNull;
    }

    public boolean isTrim() {
        return trim;
    }

    public boolean isNullsLast() {
        return nullsLast;
    }

    public String getSplitToList() {
        return splitToList;
    }

    public void setUseCompare(boolean useCompare) {
        this.useCompare = useCompare;
    }

    public boolean isUseCompare() {
        return useCompare;
    }

    /**
     * 获取处理后的值
     */
    public V getValue(T item) {
        V value = apply(item);
        if (value instanceof String) {
            if (blankToNull && PrivateUtils.isBlank(value.toString())) {
                return null;
            }
            if (trim) {
                return (V) value.toString().trim();
            }
        }
        return value;
    }

    /**
     * 获取实际值
     */
    public abstract V apply(T t);

    /**
     * 值比较<br>
     * 前置条件：a与b都不能为null
     */
    @SuppressWarnings("unchecked")
    public boolean equals(V a, V b) {
        // 再写一遍，此处不处理null
        if (PrivateUtils.isNotBlank(splitToList)) {
            Collection<String> listA = Arrays.asList(a.toString().split(splitToList));
            Collection<String> listB = Arrays.asList(b.toString().split(splitToList));
            return compareCollection(listA, listB);
        }
        if ((a instanceof Collection) && (b instanceof Collection)) {
            return compareCollection((Collection<Object>) a, (Collection<Object>) b);
        }
        Object sa = a;
        Object sb = b;
        if (toNumber) {
            sa = tryToNumber(a);
            sb = tryToNumber(b);
        }
        if (useCompare && sa instanceof Comparable && sb instanceof Comparable) {
            if (sa.getClass().isAssignableFrom(sb.getClass())) {
                int rs = ((Comparable) sb).compareTo(sa);
                return 0 == rs;
            } else if (sb.getClass().isAssignableFrom(sa.getClass())) {
                int rs = ((Comparable) sa).compareTo(sb);
                return 0 == rs;
            }
        }
        return sa.equals(sb);
    }

    private Object tryToNumber(Object n) {
        try {
            return new BigDecimal(n.toString());
        } catch (Exception e) {
            return n;
        }
    }

    private <E> boolean compareCollection(Collection<E> listA, Collection<E> listB) {
        if (toSet) {
            HashSet<E> setA = new HashSet<>(listA);
            HashSet<E> setB = new HashSet<>(listB);
            return setA.equals(setB);
        } else {
            if (listA.size() != listB.size()) {
                return false;
            }
            return Objects.deepEquals(listA, listB);
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DataAccessor) {
            return this.name.equals(((DataAccessor<?, ?>) obj).name);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + "-" + name;
    }

}
